home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CU Amiga Super CD-ROM 1
/
CU Amiga Magazine CD-ROM Special Edition (1995)(EMAP Images)(GB)[Issue 1995-11].iso
/
Aminet
/
comm
/
tcp
/
AmigaTCP.lha
/
AmigaTCP
/
src
/
arp.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-06-24
|
14KB
|
609 lines
/* Address Resolution Protocol (ARP) functions. Sits between IP and
* Level 2, mapping IP to Level 2 addresses for all outgoing datagrams.
*/
#include "machdep.h"
#include "mbuf.h"
#include "timer.h"
#include "iface.h"
#include "ether.h"
#include "ax25.h"
#include "arp.h"
#include "cmdparse.h"
extern int32 ip_addr; /* Our IP address */
int ec_output();
int pether(),gether();
int setcall(),psax25();
/* Table of ARP hardware types */
struct arp_type arp_type[] = {
0,
0,
0,
NULLCHAR,
NULLFP,
NULLFP,
/* 10 megabit Ethernet */
6, /* Ethernet address length */
0x800, /* Ethernet type field for IP */
0x806, /* Ethernet type field for ARP */
ether_bdcst, /* Ethernet broadcast address */
pether,
gether,
/* 3 megabit Ethernet */
0,
0,
0,
NULLCHAR,
NULLFP,
NULLFP,
/* AX.25 */
7, /* AX.25 address length */
0xCC, /* AX.25 pid field for IP */
0xCD, /* AX.25 pid field for ARP */
(char *)&ax25_bdcst, /* AX.25 broadcast address */
psax25,
setcall,
};
#define NTYPES 4
/* Hash table headers */
struct arp_tab *arp_tab[ARPSIZE];
struct arp_stat arp_stat;
/* Resolve an IP address to a hardware address; if not found,
* initiate query and return NULLCHAR. If an address is returned, the
* interface driver may send the packet; if NULLCHAR is returned,
* res_arp() will have saved the packet on its pending queue,
* so no further action (like freeing the packet) is necessary.
*/
char *
res_arp(interface,hardware,target,bp)
struct interface *interface; /* Pointer to interface block */
int16 hardware; /* Hardware type */
int32 target; /* Target IP address */
struct mbuf *bp; /* IP datagram to be queued if unresolved */
{
struct arp_tab *arp_lookup(),*arp_add();
void arp_output();
register struct arp_tab *arp;
if((arp = arp_lookup(hardware,target)) != NULLARP && arp->state == ARP_VALID)
return arp->hw_addr;
/* Create an entry and put the datagram on the
* queue pending an answer
*/
arp = arp_add(target,hardware,NULLCHAR,0);
enqueue(&arp->pending,bp);
arp_output(interface,hardware,target);
return NULLCHAR;
}
/* Handle incoming ARP packets. This is almost a direct implementation of
* the algorithm on page 5 of RFC 826, except for:
* 1. Outgoing datagrams to unresolved addresses are kept on a queue
* pending a reply to our ARP request.
* 2. The names of the fields in the ARP packet were made more mnemonic.
*/
void
arp_input(interface,bp)
struct interface *interface;
struct mbuf *bp;
{
struct arp arp;
struct arp_tab *arp_lookup(),*ap;
struct arp_type *at;
struct mbuf *htonarp();
arp_stat.recv++;
if(ntoharp(&arp,bp) == -1) /* Convert into host format */
return;
if(arp.hardware >= NTYPES){
/* Unknown hardware type, ignore */
arp_stat.badtype++;
return;
}
at = &arp_type[arp.hardware];
if(arp.protocol != at->iptype){
/* Unsupported protocol type, ignore */
arp_stat.badtype++;
return;
}
if(arp.hwalen > MAXHWALEN || arp.pralen != sizeof(int32)){
/* Incorrect protocol addr length (different hw addr lengths
* are OK since AX.25 addresses can be of variable length)
*/
arp_stat.badlen++;
return;
}
/* If this guy is already in the table, update its entry
* unless it's a manual entry (noted by the lack of a timer)
*/
ap = NULLARP; /* ap plays the role of merge_flag in the spec */
if((ap = arp_lookup(arp.hardware,arp.sprotaddr)) != NULLARP
&& ap->timer.start != 0){
ap = arp_add(arp.sprotaddr,arp.hardware,arp.shwaddr,arp.hwalen & 0xff);
}
/* See if we're the address they're looking for */
if(arp.tprotaddr == ip_addr){
if(ap == NULLARP) /* Only if not already in the table */
arp_add(arp.sprotaddr,arp.hardware,arp.shwaddr,arp.hwalen & 0xff);
if(arp.opcode == ARP_REQUEST){
/* Swap sender's and target's (us) hardware and protocol
* fields, and send the packet back as a reply
*/
bcopy(arp.shwaddr,arp.thwaddr,arp.hwalen);
/* Mark the end of the sender's AX.25 address
* in case he didn't
*/
if(arp.hardware == ARP_AX25)
arp.thwaddr[arp.hwalen-1] |= E;
bcopy(interface->hwaddr,arp.shwaddr,at->hwalen);
arp.tprotaddr = arp.sprotaddr;
arp.sprotaddr = ip_addr;
arp.opcode = ARP_REPLY;
bp = htonarp(&arp);
(*interface->output)(interface,arp.thwaddr,
interface->hwaddr,at->arptype,bp);
arp_stat.inreq++;
} else {
arp_stat.replies++;
}
}
}
/* Add an IP-addr / hardware-addr pair to the ARP table */
static
struct arp_tab *
arp_add(ip_addr,hardware,hw_addr,hw_alen)
int32 ip_addr; /* IP address, host order */
int16 hardware; /* Hardware type */
char *hw_addr; /* Hardware address, if known; NULLCHAR otherwise */
int16 hw_alen; /* Length of hardware address */
{
char *calloc(),*malloc();
struct arp_tab *arp_lookup();
void arp_drop();
struct mbuf *bp,*dequeue();
register struct arp_tab *ap;
register struct arp_type *at;
unsigned hashval,arp_hash();
at = &arp_type[hardware];
if((ap = arp_lookup(hardware,ip_addr)) == NULLARP){
/* New entry */
if((ap = (struct arp_tab *)calloc(1,sizeof(struct arp_tab))) == NULLARP)
return NULLARP;
ap->timer.func = arp_drop;
ap->timer.arg = (int *)ap;
ap->hardware = hardware;
ap->ip_addr = ip_addr;
/* Put on head of hash chain */
hashval = arp_hash(hardware,ip_addr);
ap->prev = NULLARP;
ap->next = arp_tab[hashval];
arp_tab[hashval] = ap;
if(ap->next != NULLARP){
ap->next->prev = ap;
}
}
if(hw_addr == NULLCHAR){
/* Await response */
ap->state = ARP_PENDING;
ap->timer.start = PENDTIME;
} else {
/* Response has come in, update entry and run through queue */
ap->state = ARP_VALID;
ap->timer.start = ARPLIFE;
if(ap->hw_addr != NULLCHAR)
free(ap->hw_addr);
if((ap->hw_addr = malloc(hw_alen)) == NULLCHAR){
free((char *)ap);
return NULLARP;
}
bcopy(hw_addr,ap->hw_addr,hw_alen);
/* This kludge marks the end of an AX.25 address to allow
* for optional digipeaters (insert Joan Rivers salute here)
*/
if(hardware == ARP_AX25)
ap->hw_addr[hw_alen-1] |= E;
while((bp = dequeue(&ap->pending)) != NULLBUF)
ip_route(bp,0);
}
start_timer(&ap->timer);
return ap;
}
/* Remove an entry from the ARP table */
static
void
arp_drop(ap)
register struct arp_tab *ap;
{
unsigned arp_hash();
if(ap == NULLARP)
return;
stop_timer(&ap->timer); /* Shouldn't be necessary */
if(ap->next != NULLARP)
ap->next->prev = ap->prev;
if(ap->prev != NULLARP)
ap->prev->next = ap->next;
else
arp_tab[arp_hash(ap->hardware,ap->ip_addr)] = ap->next;
if(ap->hw_addr != NULLCHAR)
free(ap->hw_addr);
free_q(&ap->pending);
free((char *)ap);
}
/* Look up the given IP address in the ARP table */
static
struct arp_tab *
arp_lookup(hardware,ip_addr)
int16 hardware;
int32 ip_addr;
{
unsigned arp_hash();
register struct arp_tab *ap;
for(ap = arp_tab[arp_hash(hardware,ip_addr)]; ap != NULLARP; ap = ap->next){
if(ap->ip_addr == ip_addr && ap->hardware == hardware)
break;
}
return ap;
}
/* Send an ARP request to resolve IP address target_ip */
static
void
arp_output(interface,hardware,target)
struct interface *interface;
int16 hardware;
int32 target;
{
struct arp arp;
struct mbuf *bp,*htonarp();
struct arp_type *at;
at = &arp_type[hardware];
if(interface->output == NULLFP)
return;
arp.hardware = hardware;
arp.protocol = at->iptype;
arp.hwalen = at->hwalen;
arp.pralen = sizeof(int32);
arp.opcode = ARP_REQUEST;
bcopy(interface->hwaddr,arp.shwaddr,at->hwalen);
arp.sprotaddr = ip_addr;
bzero(arp.thwaddr,at->hwalen);
arp.tprotaddr = target;
bp = htonarp(&arp);
(*interface->output)(interface,at->bdcst,
interface->hwaddr,at->arptype,bp);
arp_stat.outreq++;
}
/* Hash a {hardware type, IP address} pair */
static
unsigned
arp_hash(hardware,ip_addr)
int16 hardware;
int32 ip_addr;
{
register unsigned hashval;
hashval = hardware;
hashval ^= hiword(ip_addr);
hashval ^= loword(ip_addr);
hashval %= ARPSIZE;
return hashval;
}
/* Copy a host format arp structure into mbuf for transmission */
#ifdef AMIGA
/*
* We play some dirty tricks here. Since the AMIGA is a 68000 based
* machine, it doesn't take kindly to doing word and long word stores
* on odd address boundaries. We'll use bcopy() instead. We can do
* this simply because the 68000 is a big-endian machine, and we don't
* need to convert to network byte order. This is ugly.
*/
#endif
static
struct mbuf *
htonarp(arp)
register struct arp *arp;
{
struct mbuf *bp;
register char *buf;
if(arp == (struct arp *)NULL)
return NULLBUF;
if((bp = alloc_mbuf(sizeof(struct arp))) == NULLBUF)
return NULLBUF;
buf = bp->data;
*(int16 *)buf = htons(arp->hardware);
buf += sizeof(int16);
*(int16 *)buf = htons(arp->protocol);
buf += sizeof(int16);
*buf++ = arp->hwalen;
*buf++ = arp->pralen;
*(int16 *)buf = htons(arp->opcode);
buf += sizeof(int16);
bcopy(arp->shwaddr,buf,arp->hwalen);
buf += arp->hwalen;
#ifndef AMIGA
*(int32 *)buf = htonl(arp->sprotaddr);
#else
/* we've been alright up to now, but arp->hwalen may have been
odd, so we don't know if buf is word aligned any more! */
bcopy(&arp->sprotaddr, buf, sizeof(int32));
#endif
buf += sizeof(int32);
bcopy(arp->thwaddr,buf,arp->hwalen);
buf += arp->hwalen;
#ifndef AMIGA
*(int32 *)buf = htonl(arp->tprotaddr);
#else
bcopy(&arp->tprotaddr, buf, sizeof(int32));
#endif
buf += sizeof(int32);
bp->cnt = buf - bp->data;
return bp;
}
/* Convert an incoming ARP packet into a host-format structure */
static
int
ntoharp(arp,bp)
register struct arp *arp;
struct mbuf *bp;
{
if(arp == (struct arp *)NULL || bp == NULLBUF)
return -1;
pullup(&bp,(char *)&arp->hardware,sizeof(int16));
arp->hardware = ntohs(arp->hardware);
pullup(&bp,(char *)&arp->protocol,sizeof(int16));
arp->protocol = ntohs(arp->protocol);
pullup(&bp,(char *)&arp->hwalen,sizeof(char));
pullup(&bp,(char *)&arp->pralen,sizeof(char));
pullup(&bp,(char *)&arp->opcode,sizeof(int16));
arp->opcode = ntohs(arp->opcode);
pullup(&bp,arp->shwaddr,arp->hwalen);
pullup(&bp,(char *)&arp->sprotaddr,sizeof(int32));
arp->sprotaddr = ntohl(arp->sprotaddr);
pullup(&bp,arp->thwaddr,arp->hwalen);
pullup(&bp,(char *)&arp->tprotaddr,sizeof(int32));
arp->tprotaddr = ntohl(arp->tprotaddr);
free_p(bp);
return 0;
}
#ifdef TRACE
char *arptypes[] = {
NULLCHAR,
"Ethernet",
"Exp Ethernet",
"AX.25",
"Pronet",
"Chaos"
};
int doarpadd(),doarpdrop();
struct cmds arpcmds[] = {
"add", doarpadd, 4,
"usage: arp add <ip addr> ether|ax25 <callsign|ether addr>",
"arp add failed",
"drop", doarpdrop, 3,
"usage: arp drop <ip addr> ether|ax25",
"not in table",
NULLCHAR, NULLFP, 0,
"arp subcommands: add, drop",
NULLCHAR,
};
int
doarp(argc,argv)
int argc;
char *argv[];
{
if(argc < 2){
dumparp();
return 0;
}
return subcmd(arpcmds,argc,argv);
}
static
doarpadd(argc,argv)
int argc;
char *argv[];
{
int hardware,hwalen,i;
int32 addr,aton();
char *malloc(),*hwaddr;
int naddr;
struct arp_tab *ap;
struct arp_type *at;
struct ax25_addr *axp;
addr = aton(argv[1]);
/* This is a kludge. It really ought to be table driven */
switch(tolower(argv[2][0])){
case 'e': /* "ether" */
hardware = ARP_ETHER;
naddr = 1;
break;
case 'a': /* "ax25" */
hardware = ARP_AX25;
naddr = argc - 3;
break;
default:
printf("unknown hardware type \"%s\"\r\n",argv[2]);
return -1;
}
/* If an entry already exists, clear it */
if((ap = arp_lookup(hardware,addr)) != NULLARP)
arp_drop(ap);
at = &arp_type[hardware];
/* Allocate buffer for hardware address and fill with remaining args */
hwalen = at->hwalen * naddr;
if((hwaddr = malloc(hwalen)) == NULLCHAR){
printf("No space\r\n");
return 0;
}
(*at->scan)(hwaddr,argv[3]); /* Destination address */
/* Special hackery to handle a series of AX.25 digipeaters */
if(hardware == ARP_AX25){
axp = (struct ax25_addr *)hwaddr;
for(i=1;i<naddr;i++){
/* Set E bit only on last AX.25 call */
axp->ssid &= ~E;
/* axp++; */
axp = (struct ax25_addr *) ((char *)axp + AXALEN);
(*at->scan)((char *)axp,argv[3+i]);
}
}
ap = arp_add(addr,hardware,hwaddr,hwalen); /* Put in table */
free(hwaddr); /* Clean up */
stop_timer(&ap->timer); /* Make entry permanent */
ap->timer.count = ap->timer.start = 0;
}
/* Remove an ARP entry */
static
doarpdrop(argc,argv)
int argc;
char *argv[];
{
int hardware;
int32 addr,aton();
struct arp_tab *ap;
addr = aton(argv[1]);
/* This is a kludge. It really ought to be table driven */
switch(tolower(argv[2][0])){
case 'e': /* "ether" */
hardware = ARP_ETHER;
break;
case 'a': /* "ax25" */
hardware = ARP_AX25;
break;
default:
hardware = 0;
break;
}
if((ap = arp_lookup(hardware,addr)) == NULLARP)
return -1;
arp_drop(ap);
return 0;
}
/* Dump ARP table */
static
dumparp()
{
register int i;
extern struct arp_stat arp_stat;
register struct arp_tab *ap;
char e[128];
char *inet_ntoa();
printf("received %u badtype %u reqst in %u replies %u reqst out %u\r\n",
arp_stat.recv,arp_stat.badtype,arp_stat.inreq,
arp_stat.replies,arp_stat.outreq);
printf("IP addr Type Time Q Addr\r\n");
for(i=0;i<ARPSIZE;i++){
for(ap = arp_tab[i];ap != (struct arp_tab *)NULL;ap = ap->next){
printf("%-16s",inet_ntoa(ap->ip_addr));
printf("%-9s",arptypes[ap->hardware]);
printf("%-5ld",ap->timer.count*(long)MSPTICK/1000);
if(ap->state == ARP_PENDING)
printf("%-2u",len_q(ap->pending));
else
printf(" ");
if(ap->state == ARP_VALID){
if(arp_type[ap->hardware].format != NULLFP){
(*arp_type[ap->hardware].format)(e,ap->hw_addr);
} else {
e[0] = '\0';
}
printf("%s",e);
} else {
printf("[unknown]");
}
printf("\r\n");
}
}
return 0;
}
/* Dump ARP packets (incomplete) */
static char *hwtypes[] = {
"",
"10 Mb Ethernet",
"3 Mb Ethernet",
"AX.25",
NULLCHAR,
};
#define NHWTYPES 4
arp_dump(bp)
struct mbuf *bp;
{
struct arp arp;
struct mbuf *tbp;
char *inet_ntoa();
if(bp == NULLBUF)
return;
/* Make temporary copy */
dup_p(&tbp,bp,0,len_mbuf(bp));
ntoharp(&arp,tbp);
if(arp.hardware < NHWTYPES)
printf("ARP: hwtype %s",hwtypes[arp.hardware]);
else
printf("ARP: hwtype %u",arp.hardware);
printf(" prot 0x%x hwlen %u prlen %u",
arp.protocol,arp.hwalen,arp.pralen);
switch(arp.opcode){
case ARP_REQUEST:
printf(" op REQUEST");
break;
case ARP_REPLY:
printf(" op REPLY");
break;
default:
printf(" op %u",arp.opcode);
break;
}
printf(" target %s\r\n",inet_ntoa(arp.tprotaddr));
}
#endif